# config/redis_cluster_config.py
"""
Redis集群配置
支持单节点、哨兵、集群三种模式
"""

from typing import Dict, Any, List, Optional
from pydantic import BaseModel, Field
from loguru import logger
from utils.logger import global_logger

from utils.constants import RedisMode


class RedisClusterConfig(BaseModel):
    """Redis集群配置模型"""
    
    # 基础配置
    mode: str = Field(default=RedisMode.SINGLE, description="Redis模式: single/cluster/sentinel")
    hosts: List[str] = Field(default=["localhost"], description="Redis主机列表")
    port: int = Field(default=6379, description="Redis端口")
    password: Optional[str] = Field(default=None, description="Redis密码")
    db: int = Field(default=0, description="数据库编号")
    
    # 连接池配置
    max_connections: int = Field(default=100, description="最大连接数")
    min_connections: int = Field(default=10, description="最小连接数")
    connection_timeout: float = Field(default=5.0, description="连接超时时间(秒)")
    socket_timeout: float = Field(default=5.0, description="套接字超时时间(秒)")
    
    # 重试配置
    retry_attempts: int = Field(default=3, description="重试次数")
    retry_delay: float = Field(default=1.0, description="重试延迟(秒)")
    retry_backoff: float = Field(default=2.0, description="重试退避倍数")
    
    # 集群特定配置
    cluster_max_redirects: int = Field(default=5, description="集群最大重定向次数")
    cluster_readonly_mode: bool = Field(default=False, description="集群只读模式")
    
    # 哨兵特定配置
    sentinel_master_name: str = Field(default="mymaster", description="哨兵主节点名称")
    sentinel_socket_timeout: float = Field(default=5.0, description="哨兵套接字超时时间")
    sentinel_connection_timeout: float = Field(default=5.0, description="哨兵连接超时时间")
    
    # 健康检查配置
    health_check_interval: int = Field(default=30, description="健康检查间隔(秒)")
    health_check_timeout: float = Field(default=3.0, description="健康检查超时时间(秒)")
    
    # 性能监控配置
    enable_metrics: bool = Field(default=True, description="启用性能指标收集")
    slow_query_threshold_ms: int = Field(default=100, description="慢查询阈值(毫秒)")
    
    class Config:
        extra = "allow"


# 导入环境变量处理
import os

# 预定义配置模板
REDIS_CLUSTER_TEMPLATES = {
    "development": RedisClusterConfig(
        mode=RedisMode.SINGLE,
        hosts=[os.getenv("REDIS_HOST", "192.168.64.100")],
        port=int(os.getenv("REDIS_PORT", "6379")),
        password=os.getenv("REDIS_PASSWORD", ""),
        db=int(os.getenv("REDIS_DB", "0")),
        max_connections=50,
        connection_timeout=3.0,
        retry_attempts=2,
        health_check_interval=60
    ),
    
    "testing": RedisClusterConfig(
        mode=RedisMode.CLUSTER,
        hosts=["redis-node1", "redis-node2", "redis-node3"],
        port=6379,
        max_connections=100,
        connection_timeout=5.0,
        retry_attempts=3,
        cluster_max_redirects=5,
        health_check_interval=30
    ),
    
    "production": RedisClusterConfig(
        mode=RedisMode.CLUSTER,
        hosts=[
            "redis-cluster-1.example.com",
            "redis-cluster-2.example.com",
            "redis-cluster-3.example.com",
            "redis-cluster-4.example.com",
            "redis-cluster-5.example.com",
            "redis-cluster-6.example.com"
        ],
        port=6379,
        password="${REDIS_PASSWORD}",  # 从环境变量读取
        max_connections=500,
        min_connections=50,
        connection_timeout=5.0,
        socket_timeout=5.0,
        retry_attempts=5,
        retry_delay=1.0,
        retry_backoff=2.0,
        cluster_max_redirects=10,
        cluster_readonly_mode=False,
        health_check_interval=30,
        health_check_timeout=3.0,
        enable_metrics=True,
        slow_query_threshold_ms=50
    ),
    
    "production_sentinel": RedisClusterConfig(
        mode=RedisMode.SENTINEL,
        hosts=[
            "redis-sentinel-1.example.com",
            "redis-sentinel-2.example.com",
            "redis-sentinel-3.example.com"
        ],
        port=26379,
        password="${REDIS_PASSWORD}",
        sentinel_master_name="mymaster",
        max_connections=500,
        min_connections=50,
        connection_timeout=5.0,
        socket_timeout=5.0,
        retry_attempts=5,
        retry_delay=1.0,
        retry_backoff=2.0,
        sentinel_socket_timeout=5.0,
        sentinel_connection_timeout=5.0,
        health_check_interval=30,
        health_check_timeout=3.0,
        enable_metrics=True,
        slow_query_threshold_ms=50
    )
}


def load_redis_cluster_config(env: str = "development", 
                            custom_config: Dict[str, Any] = None) -> RedisClusterConfig:
    """
    加载Redis集群配置
    
    Args:
        env: 环境名称
        custom_config: 自定义配置覆盖
    
    Returns:
        RedisClusterConfig实例
    """
    try:
        # 获取基础配置模板
        if env in REDIS_CLUSTER_TEMPLATES:
            config = REDIS_CLUSTER_TEMPLATES[env].copy()
            global_logger.info(f"使用{env}环境Redis配置模板")
        else:
            config = REDIS_CLUSTER_TEMPLATES["development"].copy()
            global_logger.warning(f"未知环境'{env}'，使用development配置")
        
        # 应用自定义配置
        if custom_config:
            config_data = config.dict()
            config_data.update(custom_config)
            config = RedisClusterConfig(**config_data)
            global_logger.info("应用了自定义Redis配置")
        
        # 替换环境变量
        config = _resolve_environment_variables(config)
        
        global_logger.info(f"Redis集群配置加载完成:")
        global_logger.info(f"  模式: {config.mode}")
        global_logger.info(f"  主机: {config.hosts}")
        global_logger.info(f"  端口: {config.port}")
        global_logger.info(f"  最大连接数: {config.max_connections}")
        
        return config
        
    except Exception as e:
        global_logger.error(f"加载Redis集群配置失败: {e}")
        raise


def _resolve_environment_variables(config: RedisClusterConfig) -> RedisClusterConfig:
    """解析配置中的环境变量"""
    import os
    import re
    
    config_dict = config.dict()
    
    def resolve_value(value):
        """递归解析值中的环境变量"""
        if isinstance(value, str):
            # 匹配 ${VAR_NAME} 或 $VAR_NAME 格式
            pattern = r'\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z0-9_]*)'
            matches = re.findall(pattern, value)
            
            for match in matches:
                var_name = match[0] if match[0] else match[1]
                env_value = os.getenv(var_name)
                if env_value is not None:
                    value = value.replace(f"${{{var_name}}}", env_value)
                    value = value.replace(f"${var_name}", env_value)
                else:
                    global_logger.warning(f"环境变量'{var_name}'未设置")
            
            return value
        elif isinstance(value, list):
            return [resolve_value(item) for item in value]
        elif isinstance(value, dict):
            return {k: resolve_value(v) for k, v in value.items()}
        else:
            return value
    
    # 解析所有配置值
    resolved_config = {}
    for key, value in config_dict.items():
        resolved_config[key] = resolve_value(value)
    
    return RedisClusterConfig(**resolved_config)


def get_redis_cluster_config_for_mode(mode: str, 
                                    hosts: List[str] = None,
                                    password: str = None) -> RedisClusterConfig:
    """
    根据模式获取Redis集群配置
    
    Args:
        mode: Redis模式 (single/cluster/sentinel)
        hosts: 主机列表
        password: 密码
    
    Returns:
        RedisClusterConfig实例
    """
    if mode == RedisMode.CLUSTER:
        return RedisClusterConfig(
            mode=mode,
            hosts=hosts or ["localhost"],
            port=6379,
            password=password,
            max_connections=200,
            cluster_max_redirects=10
        )
    elif mode == RedisMode.SENTINEL:
        return RedisClusterConfig(
            mode=mode,
            hosts=hosts or ["localhost"],
            port=26379,
            password=password,
            sentinel_master_name="mymaster",
            max_connections=200
        )
    else:  # single
        return RedisClusterConfig(
            mode=RedisMode.SINGLE,
            hosts=hosts or ["localhost"],
            port=6379,
            password=password,
            max_connections=100
        )


# 配置验证函数
def validate_redis_cluster_config(config: RedisClusterConfig) -> bool:
    """验证Redis集群配置"""
    try:
        # 基础验证
        if not config.hosts:
            global_logger.error("Redis主机列表不能为空")
            return False
        
        if config.port <= 0 or config.port > 65535:
            global_logger.error(f"Redis端口无效: {config.port}")
            return False
        
        if config.max_connections <= 0:
            global_logger.error(f"最大连接数必须大于0: {config.max_connections}")
            return False
        
        # 模式特定验证
        if config.mode == RedisMode.CLUSTER:
            if len(config.hosts) < 3:
                global_logger.warning(f"Redis集群建议至少3个节点，当前只有{len(config.hosts)}个")
        
        elif config.mode == RedisMode.SENTINEL:
            if len(config.hosts) < 2:
                global_logger.warning(f"Redis哨兵建议至少2个哨兵节点，当前只有{len(config.hosts)}个")
            if not config.sentinel_master_name:
                global_logger.error("Redis哨兵模式必须指定主节点名称")
                return False
        
        global_logger.info("Redis集群配置验证通过")
        return True
        
    except Exception as e:
        global_logger.error(f"Redis集群配置验证失败: {e}")
        return False


# 配置示例和文档
def print_redis_cluster_config_examples():
    """打印配置示例"""
    print("Redis集群配置示例:")
    print("=" * 50)
    
    for env, config in REDIS_CLUSTER_TEMPLATES.items():
        print(f"\n{env.upper()} 环境:")
        print(f"  模式: {config.mode}")
        print(f"  主机: {config.hosts}")
        print(f"  端口: {config.port}")
        print(f"  最大连接数: {config.max_connections}")
        print(f"  重试次数: {config.retry_attempts}")
    
    print("\n使用示例:")
    print("  config = load_redis_cluster_config('production')")
    print("  client = RedisClusterClient(config.dict())")
    print("  await client.initialize()")


if __name__ == "__main__":
    print_redis_cluster_config_examples()